/******************************************/
/*Version Thread du tri a bulle************/
/*Mathieu VALTRINE*************************/
/*Vincent DI CARLO*************************/
/******************************************/

#include "arch.h"
#include<time.h>
#include<iostream>

//user program
Global_id gid1;
Global_id gid2;
Coll_id cid;

#define N 5

class Table
{
   public:
        int tab[N];
};


ChanRD<Table> *proc_to_left;
ChanRD<Table> *proc_to_right;

ChanLD<int> *chan[2];

void init_gid(int, char **)
{
   gid1.set();
   gid2.set();
   cid.set();
}

class Id
{
   public:
        int processor_Id;
        int process_Id;
};


//On declare nos tableaux en global
Table table;
Table t;
int m[2*N];

        
        //fonction de tri rapide
        void tri_bulle(int tableau[],int longueur)
     	  {
         	int i, inversion;

     			do
       		{
       			inversion=0;

       			for(i=0;i<longueur-1;i++)
          		{
          			if (tableau[i]>tableau[i+1])
             		{
             		int tmp=tableau[i];
             		tableau[i]=tableau[i+1];
             		tableau[i+1]=tmp;
             		inversion=1;
             		}
          		}
       		}
     			while(inversion);
     		}
     		
     		//procedure d'interclassement
     		void interclasser(int tableau[],int t[],int m[],int lg1,int lg2)
     		{
     			int i=0,j=0,k=0;

     			while((i<lg1)&&(j<lg2))
     			{
     					if(tableau[i]>t[j])
     					{
     						m[k]=t[j];
     						++j;
     					}

     					else
     					{

     						m[k]=tableau[i];
     						++i;
     					}
     					
     				++k;
     			}
			
					
     			if(i==lg1)
     			{

     				while(j<lg2)
     				{
     					
     					m[k]=t[j];
     					++j;
     					++k;
     				}
     			}
     			else
     			{

     					while(i<lg1)
     					{
     						m[k]=tableau[i];
     						++i;
     						++k;
     					}
     			}
     			
     		}
        	


class Send_proc : public S_Thread
{
	public:
	int pid;
   int step;	
   Id id;
   int message;

			Send_proc(int _pid) : S_Thread((Smf0_ptr)&Send_proc::body)
		{ pid=_pid;
		}	
      
	void body()
	{	
				
			for(int i=0;i<Sdf::PROC_NB;++i)
			{
	
				//Si proc_id est pair				
				if(!(Sdf::PROC_ID%2))
				{	
					//phase 1
					if(Sdf::PROC_ID<Sdf::PROC_NB-1)
					{ 
		           chan[0]->recv(message);//reception de l'autorisation de Traitement
		           proc_to_right->send(table);//envoi la table vers proc_to_right
		           chan[0]->send(pid);//confirme a Traitement que la table a bien ete envoyee
														           
					}
					
					//phase 2
					if(Sdf::PROC_ID>0)
					{
		           chan[0]->recv(message);
		           proc_to_left->send(table);		           
		           chan[0]->send(pid);
					}
					
				}	
					
				else
				{
					chan[0]->recv(message);
          	   proc_to_left->send(table);					
         	   chan[0]->send(pid);          	   
         	   
					if(Sdf::PROC_ID<Sdf::PROC_NB-1)
					{
					  chan[0]->recv(message);
		           proc_to_right->send(table);		           
		           chan[0]->send(pid);
					}	       	   
					
         	}	
					
			}		
					
		
	}
};

class Recv_proc : public S_Thread
{
	public:
		int message;
		int pid;

			Recv_proc(int _pid) : S_Thread((Smf0_ptr)&Recv_proc::body)
		{ pid=_pid;
		}

	
	void body()
	{	
	

		for(int i=0;i<Sdf::PROC_NB;++i)
		{
			if(!(Sdf::PROC_ID%2))
			{
				//phase 1
				chan[1]->recv(message);//reçoit l'autorisation de Traitement
				proc_to_left->recv(t);//reçoit le tableau de proc_to_left
				chan[1]->send(pid);//confirme a Traitement que le tableau a bien ete reçu
				
				//phase2
				if(Sdf::PROC_ID>0)
				{
					chan[1]->recv(message);
					proc_to_right->recv(t);
					chan[1]->send(pid);					
				}								
			}
			
			else
			{
				//phase 1 
				chan[1]->recv(message);
				proc_to_right->recv(t);
				chan[1]->send(pid);	
				
				//phase 2
				if(Sdf::PROC_ID<Sdf::PROC_NB-1)							
				{
					chan[1]->recv(message);
					proc_to_left->recv(t);
					chan[1]->send(pid);				
				}
			}
			
		}
		
	}
};

class Traitement: public S_Thread
{
	public:
		int message;
		int pid;

		Traitement(int _pid) : S_Thread((Smf0_ptr)&Traitement::body)
		{ pid=_pid;
		}

		void body()
		{
	
		
		for(int i=0;i<Sdf::PROC_NB;++i)
		{
			if(!(Sdf::PROC_ID%2))
			{				
				chan[0]->send(pid);	
				fprintf(Sdf::trace, "node%d: traitement envoie a Send_Proc %d\n",Sdf::PROC_ID,pid);
				fflush(Sdf::trace);					
										
				chan[1]->send(pid);
				fprintf(Sdf::trace, "node%d: traitement envoie a Recv_Proc %d\n",Sdf::PROC_ID,pid);
				fflush(Sdf::trace);					
								
				chan[1]->recv(message);				
				fprintf(Sdf::trace, "node%d: traitement a reçu de Recv_proc le message: %d \n",Sdf::PROC_ID,message);
				fflush(Sdf::trace);						
													
				
				interclasser(table.tab,t.tab,m,N,N);
				

				/********************* Affichage de la table m ***********************************/
				fprintf(Sdf::trace, "node%d: traitement phase 1 : m= ", Sdf::PROC_ID);
				fflush(Sdf::trace);         		      		        	
        	
			   for(int k=0;k<2*N;k++)
        		{
					fprintf(Sdf::trace, "%d ", m[k]);
					fflush(Sdf::trace);           		
				}
				
				fprintf(Sdf::trace, "\n");
				fflush(Sdf::trace); 						
						
				chan[0]->recv(message);//send  a bien envoye la table				
				fprintf(Sdf::trace, "node%d: traitement a reçu de Send_proc le message: %d \n",Sdf::PROC_ID,message);
				fflush(Sdf::trace);						
		
				fprintf(Sdf::trace, "On recupere la moitie basse de m\n");
				fflush(Sdf::trace); 	
														
				
				for(int k=0;k<N;++k)
					table.tab[k]=m[k];
			

			//deuxieme phase
			if(Sdf::PROC_ID>0)
			{
				chan[0]->send(pid);			
				fprintf(Sdf::trace, "node%d: traitement envoie a Send_Proc %d\n",Sdf::PROC_ID,pid);
				fflush(Sdf::trace);					
							
				chan[1]->send(pid);				
				fprintf(Sdf::trace, "node%d: traitement envoie a Recv_Proc %d\n",Sdf::PROC_ID,pid);
				fflush(Sdf::trace);	
								
				chan[1]->recv(message);
				fprintf(Sdf::trace, "node%d: traitement a reçu de Recv_proc le message: %d \n",Sdf::PROC_ID,message);
				fflush(Sdf::trace);				
				
				
				
				interclasser(table.tab,t.tab,m,N,N);
				

				/********************* Affichage de la table m ***********************************/
				fprintf(Sdf::trace, "node%d: traitement phase 2 : m= ", Sdf::PROC_ID);
				fflush(Sdf::trace);  
       		      		        	
        		for(int k=0;k<2*N;k++)
        		{
						fprintf(Sdf::trace, "%d ", m[k]);
						fflush(Sdf::trace);           		
				}
				
				fprintf(Sdf::trace, "\n");
				fflush(Sdf::trace); 						
					
				chan[0]->recv(message);//send  a bien envoye la table					
				fprintf(Sdf::trace, "node%d: traitement a reçu de Send_Proc le message: %d \n",Sdf::PROC_ID,message);
				fflush(Sdf::trace);									
				
				fprintf(Sdf::trace, "On recupere la moitie haute de m\n");
				fflush(Sdf::trace); 										
									
				for(int i=N;i<2*N;i++)
						table.tab[i-N]=m[i];
															
				
			}
			
			}
			
			else
			{
				chan[0]->send(pid);						
				fprintf(Sdf::trace, "node%d: traitement envoie a Send_Proc %d\n",Sdf::PROC_ID,pid);
				fflush(Sdf::trace);	
															
				chan[1]->send(pid);							
				fprintf(Sdf::trace, "node%d: traitement envoie a Recv_proc %d \n",Sdf::PROC_ID,pid);
				fflush(Sdf::trace);	
										
				chan[1]->recv(message);				
				fprintf(Sdf::trace, "node%d: traitement a reçu de Recv_proc le message: %d \n",Sdf::PROC_ID,message);
				fflush(Sdf::trace);									
				
				interclasser(table.tab,t.tab,m,N,N);
				

				/********************* Affichage de la table m ***********************************/
				fprintf(Sdf::trace, "node%d: traitement phase 1 : m= ", Sdf::PROC_ID);
				fflush(Sdf::trace);         		      		        	
        	
			   for(int k=0;k<2*N;k++)
        		{
						fprintf(Sdf::trace, "%d ", m[k]);
						fflush(Sdf::trace);           		
				}	
					
				fprintf(Sdf::trace, "\n");	
				fflush(Sdf::trace); 	
										
				chan[0]->recv(message);//send  a bien envoye la table				
				fprintf(Sdf::trace, "node%d: traitement a reçu de Send_proc le message: %d \n",Sdf::PROC_ID,message);
				fflush(Sdf::trace);					
				
								
				fprintf(Sdf::trace, "On recupere la moitie haute de m\n");
				fflush(Sdf::trace); 							
																				
					for(int i=N;i<2*N;++i)
						table.tab[i-N]=m[i];																
				
				

				//deuxieme phase
	 			if(Sdf::PROC_ID<Sdf::PROC_NB-1)	
	 			{
	 			
					chan[0]->send(pid);						
					fprintf(Sdf::trace, "node%d: traitement envoie a Send_proc %d \n",Sdf::PROC_ID,pid);
					fflush(Sdf::trace);
											
					chan[1]->send(pid);			
					fprintf(Sdf::trace, "node%d: traitement envoie a Recv_proc %d \n",Sdf::PROC_ID,pid);
					fflush(Sdf::trace);					
					
					chan[1]->recv(message);			
				 	fprintf(Sdf::trace, "node%d: traitement a reçu de Recv_proc le message: %d \n",Sdf::PROC_ID,message);
					fflush(Sdf::trace);														
				
					interclasser(table.tab,t.tab,m,N,N);	
					

					/********************* Affichage de la table m ***********************************/
					fprintf(Sdf::trace, "node%d: traitement phase 2 : m= ", Sdf::PROC_ID);
					fflush(Sdf::trace);         		      		        	
        	
			      for(int k=0;k<2*N;k++)
        			{
						fprintf(Sdf::trace, "%d ", m[k]);
						fflush(Sdf::trace);           		
					}
					
					fprintf(Sdf::trace, "\n");
					fflush(Sdf::trace); 	
							
					chan[0]->recv(message);//send  a bien envoye la table					
				 	fprintf(Sdf::trace, "node%d: traitement a reçu de Send_proc le message:  %d\n",Sdf::PROC_ID,message);
					fflush(Sdf::trace);						
						
					fprintf(Sdf::trace, "On recupere la moitie basse de m\n");
					fflush(Sdf::trace); 												
				
					for(int i=0;i<N;i++)
						table.tab[i]=m[i];						
								 				
	 			}
				
			}
  			       	
			
		}
		
		}
		

};

class Root_proc : public Thread
{
   public:
        Root_proc() : Thread((Tmf0_ptr)&Root_proc::body){}
        
			void body()
			{
				//on initialise le tableau
	         srand(Sdf::PROC_ID*10);  
	         
	         for(int i=0;i<N;i++)
        			table.tab[i]=rand()%100;
        		
        		// Affichage du tableau non trie
				fprintf(Sdf::trace, "node%d: Avant tri rapide : table= ", Sdf::PROC_ID);
				fflush(Sdf::trace);	
				
        		for(int i=0;i<N;i++)
        		{
					fprintf(Sdf::trace, "%d ", table.tab[i]);
					fflush(Sdf::trace);           		
				}
								        	
				fprintf(Sdf::trace, "\n");
				fflush(Sdf::trace); 
				
				tri_bulle(table.tab,N);
				
				
				// Affichage du tableau trie
				fprintf(Sdf::trace, "node%d: Apres tri rapide : table= ", Sdf::PROC_ID);
				fflush(Sdf::trace);
				
				for(int i=0;i<N;i++)
        		{
					fprintf(Sdf::trace, "%d ", table.tab[i]);
					fflush(Sdf::trace);           		
				}
					
									       	
        	
				fprintf(Sdf::trace, "\n");
				fflush(Sdf::trace); 					

				//creation du canal 				
         	int left_id = ((Sdf::PROC_ID > 0)? Sdf::PROC_ID-1 : Sdf::NOPID);
                proc_to_left = new ChanRD<Table>(gid1, left_id);
                
				int right_id = ((Sdf::PROC_ID < Sdf::PROC_NB-1)? Sdf::PROC_ID+1 : Sdf::NOPID);
                proc_to_right = new ChanRD<Table>(gid2, right_id);
                
                //on cree les 2 canaux locaux
                chan[0] = new ChanLD<int>;
                chan[1] = new ChanLD<int>;                
            
            //on cree et on lance les 3 threads
            Send_proc * proc1 = new Send_proc(0);
				Recv_proc * proc2 = new Recv_proc(1);
				Traitement* proc3=new Traitement(2);
				S_Thread * proc_tabl[3] = {proc1, proc2,proc3};
				int pri[3] = {1, 1, 1};
				par(3, proc_tabl, pri);   
				
            delete proc1, delete proc2, delete proc3;   //destruction des Threads 
            
            delete chan[0],delete chan[1],delete proc_to_left, delete proc_to_right;//destruction des canaux 
            
							
						fprintf(Sdf::trace, "\n********** Table finale de node%d, table= ", Sdf::PROC_ID);
						fflush(Sdf::trace); 			
			
			                             	
			        	for(int i=0;i<N;++i)
			        	{
							fprintf(Sdf::trace, "%d ", table.tab[i]);
							fflush(Sdf::trace);           		
						}       		       	
        	
						fprintf(Sdf::trace, " ********************************************\n\n");
						fflush(Sdf::trace);  			
	         
			}       
        

        
};        
   
MAIN(Root_proc, init_gid, Sdf::empty, Sdf::empty)   
